#!/bin/sh
# tlp-rdw - network manager dispatcher hook:
#           enable/disable radios on ifup/ifdown
#
# Copyright (c) 2025 Thomas Koch <linrunner at gmx.net> and others.
# SPDX-License-Identifier: GPL-2.0-or-later

# --- Source libraries

for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do
    # shellcheck disable=SC1090
    . "$lib" || exit 70
done

# --- Functions

check_switch_lock() { # switch listed radio devices
    # and time-lock them afterwards if actually switched
    # $1: device type where the event originated -- do nothing if its time-locked
    # $2: list of device types to switch
    # $3: on/off
    local sw_rc type

    # quit if the originating *radio* device is time-locked (not LAN)
    [ "$1" != "LAN" ] && check_timed_lock "${RDW_NM_LOCK}_$1" && return 1

    for type in $2; do
        if [ -n "$type" ] && [ "$type" != "$1" ]; then
            # device type is valid and not the originating one
            # --> do switch with state change lock
            device_switch "$type" "$3" "${RDW_NM_LOCK}_${type}" "$RDW_NM_LOCKTIME"; sw_rc=$?

            if [ "$sw_rc" = "4" ]; then
                # switch failed, NetworkManager may be "asleep" -> schedule repeat:
                # open a detached subshell, wait 2 secs and respawn ourselves,
                # no more than two repetitions.
                case "$repeats" in
                    "") repeats="2" ;;
                    2)  repeats="1" ;;
                    *)  repeats=""  ;;
                esac
                if [ -n "$repeats" ]; then
                    echo_debug "nm" "+++ tlp_rdw_nm(${iface}).${action}.nm_seems_asleep: repeats=$repeats"
                    ( sleep 2; $0 "$iface" "$action" "$repeats" < /dev/null > /dev/null ) &
                    do_exit 0
                fi
            fi
        fi
    done

    return 0
}

save_iface_type () { # save interface type -- $1: interface; $2: type
    # rc: 0=saved/1=error
    [ -d "$NETD/$1" ] && { printf '%s\n' "$2" > "$RUNDIR/${1}.itype"; } 2> /dev/null
    return $?
}

get_iface_type () { # get saved interface type -- $1: interface
    # rc: 0=saved state found/1=not found
    # retval: $itype
    local rc

    itype=$(read_sysf "$RUNDIR/${1}.itype"); rc=$?
    rm -f "$RUNDIR/${1}.itype"
    return $rc
}

echo_env () {
    # record environment
    if [ "$X_USB_ENV_TRACE" = "1" ]; then
        echo_debug "nm" "tlp_rdw_nm.env: $(printenv)"
    fi
}

# --- MAIN
# shellcheck disable=SC2034
_bgtask=1

# read configuration: quit on error, trace allowed
read_config 0

# quit if TLP disabled
check_tlp_enabled || do_exit 0

# quit if RDW disabled
check_run_flag "$RDW_KILL" && do_exit 0
add_sbin2path

# get args
iface="$1"
action="$2"
repeats="$3"
itype=""

case "$action" in
    up|down) # interface up/down
        # quit for invalid interfaces
        if [ -z "$iface" ] || [ "$iface" = "none" ]; then
            echo_debug "nm" "tlp_rdw_nm($iface).${action}.no_interface"
            echo_env
            do_exit 0
        fi
        #  quit for virtual interfaces (up action)
        if [ "$action" = "up" ] && readlink "$NETD/${iface}" | grep -q '/virtual/'; then
            # save type for down action where $NETD/$iface won't be there anymore
            save_iface_type "$iface" virtual
            echo_debug "nm" "tlp_rdw_nm($iface).${action}.ignore_virtual"
            echo_env
            do_exit 0
        fi

        # get saved interface type (down action)
        if [ "$action" = "down" ]; then
            get_iface_type "$iface"

            # quit for virtual interfaces
            if [ "$itype" = "virtual" ]; then
                echo_debug "nm" "tlp_rdw_nm($iface).${action}.ignore_virtual"
                do_exit 0
            fi
        fi

        echo_debug "nm" "+++ tlp_rdw_nm($iface).$action: repeats=$repeats"
        echo_env
        # shellcheck disable=SC2154
        if [ -n "$_addpath" ]; then
            # shellcheck disable=SC2154
            echo_debug "path" "PATH=${_oldpath}[${_addpath}]"
        else
            # shellcheck disable=SC2154
            echo_debug "path" "PATH=${_oldpath}"
        fi

        # determine interface type
        if [ -n "$itype" ]; then
            # saved type available (down action)
            echo_debug "nm" "tlp_rdw_nm($iface).${action}: type=$itype [saved]"

        elif cmd_exists "$NMCLI"; then
            # no saved type but nmcli is available
            # --> check if nmcli dev output matches interface
            itype="$($NMCLI dev | awk '$1 ~ /^'"$iface"'$/ { print $2; }')"

            if [ -z "$itype" ]; then
                # iface is not found in nmcli dev output: many WWAN devices have
                # different devices for control and the actual network connection
                # --> check if interface matches a WWAN device
                get_wwan_ifaces
                # shellcheck disable=SC2154
                if wordinlist "$iface" "$_wanifaces"; then
                    itype="wwan"
                else
                    # fallback:
                    # if interface type detection with nmcli failed, then try to
                    # deduct it using interface name: it can happen if e.g.
                    # usb network card is unplugged
                    case "$iface" in
                        en* | eth*)
                            itype="ethernet"
                            ;;

                        wl*)
                            itype="wifi"
                            ;;

                        ww*)
                            itype="wwan"
                            ;;

                        *)
                            itype="unknown"
                            ;;
                    esac
                fi
            fi

            # save interface type (up action)
            [ "$action" = "up" ] && save_iface_type "$iface" "$itype"

            echo_debug "nm" "tlp_rdw_nm($iface).${action}: type=$itype [nmcli]"

        else
            # nmcli is not available
            itype="unknown"
            echo_debug "nm" "tlp_rdw_nm($iface).${action}: type=$itype [none]"
        fi

        case "$action" in
            up) # interface up, disable configured interfaces

                case $itype in
                    *ethernet)
                        check_switch_lock LAN "$DEVICES_TO_DISABLE_ON_LAN_CONNECT" off
                        ;;

                    *wireless|wifi)
                        check_switch_lock wifi "$DEVICES_TO_DISABLE_ON_WIFI_CONNECT" off
                        ;;

                    gsm|wwan)
                        check_switch_lock wwan "$DEVICES_TO_DISABLE_ON_WWAN_CONNECT" off
                        ;;
                esac
                ;; # up

            down) # interface down, enable configured interfaces
                case $itype in
                    *ethernet)
                        check_switch_lock LAN "$DEVICES_TO_ENABLE_ON_LAN_DISCONNECT" on
                        ;;

                    *wireless|wifi)
                        check_switch_lock wifi "$DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT" on
                        ;;

                    gsm|wwan)
                        check_switch_lock wwan "$DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT" on
                        ;;
                esac
                ;; # down

        esac
        ;; # up/down

    *)
        # other calls: do nothing
        ;;

esac # action

do_exit 0
